package top.hekun.study.core.visitor.method.simple;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import top.hekun.study.core.util.MyMethodVisitor5Util;

import static org.objectweb.asm.Opcodes.*;

/**
 * @Author: 贺坤
 * @Date: 2023/5/10 10:16
 */
public class MyMethodVisitor5 extends MethodVisitor {
    private String thisClassName = this.getClass().getSimpleName();
    // 方法描述符
    private Type methodType;
    boolean isStaticMethod;

    protected MyMethodVisitor5(int api, MethodVisitor methodVisitor, String descriptor, int access) {
        super(api, methodVisitor);
        this.methodType = Type.getMethodType(descriptor);
        // 当access [方法描述符] 包含ACC_STATIC[静态表示] 的时候, access & Opcodes.ACC_STATIC >0 否则 值为0
        // true 的时候是 静态方法
        this.isStaticMethod = ((access & Opcodes.ACC_STATIC) != 0);
    }

    @Override
    public void visitCode() {
        System.out.println(thisClassName + ":visitCode");
        // 获取方法参数
        Type[] argumentTypes = methodType.getArgumentTypes();
        // 确定获取参数起始位
        int slotIndex = isStaticMethod ? 0 : 1;
        //遍历
        for (int i = 0; i < argumentTypes.length; ) {
            Type type = argumentTypes[i];
            // 获取类型
            int sort = type.getSort();
            // 获取长度
            int size = type.getSize();
            // 获取描述符
            String descriptor = type.getDescriptor();
            // 获取对应类型的操作码  , ILOAD 为int 类型加载的操作码,getOpcode会自动转换
            int opcode = type.getOpcode(ILOAD);
            visitVarInsn(opcode, slotIndex + i);
            // 调用用静态方法
            visitMethodInsn(INVOKESTATIC, Type.getType(MyMethodVisitor5Util.class).getInternalName(), "parameterPrintln", Type.getMethodDescriptor(Type.VOID_TYPE,type), false);
            // 移动标志位, 有的数据类型 如Long  占用2位长度
            i += size;
        }
        super.visitCode();

    }


    @Override
    public void visitInsn(int opcode) {
        // 在调用返回前 进行处理 出线异常
        if (opcode == Opcodes.ATHROW) {
            System.out.println(thisClassName + ":visitInsn --- 执行 异常代码前 ");
            visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            visitLdcInsn(thisClassName + "在  test2(II)D  异常啦");
            visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

        }
        // 在调用返回前 进行处理 正常返回
        if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
            Type returnType = methodType.getReturnType();
            // 判断方好时候有返回值
            if (returnType==Type.VOID_TYPE) {
                // 调用用静态方法
                visitMethodInsn(INVOKESTATIC, Type.getType(MyMethodVisitor5Util.class).getInternalName(), "returnPrintln", Type.getMethodDescriptor(Type.VOID_TYPE), false);

            }else {
            String descriptor = returnType.getDescriptor();
            //  复制返回值
            visitInsn(returnType.getOpcode(DUP));
            // 对象类型以及 数组等特殊类型可能需要单独处理
            visitMethodInsn(INVOKESTATIC, Type.getType(MyMethodVisitor5Util.class).getInternalName(), "returnPrintln", Type.getMethodDescriptor(Type.VOID_TYPE,returnType), false);

        }

        }

        super.visitInsn(opcode);
    }
}
